#ifndef FFVDP_GRING_H
#define FFVDP_GRING_H

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

typedef struct ffvdp_ring {
    // when g_head == g_tail, ring is empty
    // when g_head == (g_tail + 1), ring is full
    volatile uint32_t head;
    volatile uint32_t tail;

    // g_ring_size must be power of 2
    uint32_t ring_size;

    uint8_t *data[0];

} ffvdp_ring_t;

static inline int __attribute__((always_inline))
ffvdp_ring_empty(ffvdp_ring_t *ring) {
    return (ring->head == ring->tail) ? 1 : 0;
}

static inline int __attribute__((always_inline))
ffvdp_ring_full(ffvdp_ring_t *ring) {
    return ((ring->tail - ring->head - 1) & (ring->ring_size - 1)) == 0;
}

static inline uint32_t __attribute__((always_inline))
ffvdp_ring_count(ffvdp_ring_t *ring) {
    return (ring->head - ring->tail) & (ring->ring_size - 1);
}

static inline uint32_t __attribute__((always_inline))
ffvdp_ring_index(ffvdp_ring_t *ring, uint32_t pos) {
    return pos & (ring->ring_size - 1);
}

static inline uint32_t __attribute__((always_inline))
ffvdp_ring_head(ffvdp_ring_t *ring) {
    return ring->head & (ring->ring_size - 1);
}

static inline uint32_t __attribute__((always_inline))
ffvdp_ring_tail(ffvdp_ring_t *ring) {
    return ring->tail & (ring->ring_size - 1);
}

static inline uint32_t __attribute__((always_inline))
ffvdp_ring_continue_buf_cnt(ffvdp_ring_t *ring) {
    return ring->ring_size - ffvdp_ring_index(ring, ring->head);
}

static inline void __attribute__((always_inline))
ffvdp_ring_head_move(ffvdp_ring_t *ring, uint32_t n) {
    ring->head += n;
}

static inline void __attribute__((always_inline))
ffvdp_ring_tail_move(ffvdp_ring_t *ring, uint32_t n) {
    ring->tail += n;
}

static inline void __attribute__((always_inline))
ffvdp_ring_clear(ffvdp_ring_t *ring) {
    ffvdp_ring_tail_move(ring, ffvdp_ring_count(ring));
}

static inline void __attribute__((always_inline))
ffvdp_ring_enqueue(ffvdp_ring_t *ring, void *buf) {
    ring->data[ffvdp_ring_index(ring, ring->head)] = buf;
    __asm__ __volatile__("dmb st" : : : "memory");
    ffvdp_ring_head_move(ring, 1);
}

static inline void __attribute__((always_inline))
ffvdp_ring_dequeue(ffvdp_ring_t *ring, void **buf) {
    *buf = ring->data[ffvdp_ring_index(ring, ring->tail)];
    __asm__ __volatile__("dmb st" : : : "memory");
    ffvdp_ring_tail_move(ring, 1);
}

#if 0
static inline __attribute__((always_inline))
ffvdp_ring_t *ffvdp_ring_alloc(uint32_t ring_size) {
    ffvdp_ring_t *r;
    r = calloc(1, sizeof(ffvdp_ring_t) + sizeof(uint8_t*) * ring_size);
    if (!r) {
        return NULL;
    }
    r->ring_size = ring_size;

    return r;
}

static inline __attribute__((always_inline))
void ffvdp_ring_free(ffvdp_ring_t *r) {
    free(r);
}
#else
ffvdp_ring_t *ffvdp_ring_alloc(uint32_t ring_size);
void ffvdp_ring_free(ffvdp_ring_t *r);
#endif

#endif
